﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using FlexiCommerce.Components;
using FlexiCommerce.Details;
using FlexiCommerce.Persistence;
using FlexiCommerce.Persistence.NH;
using NHibernate;
using NHibernate.Driver;
using NHibernate.SqlTypes;



using NHibernate.Tool.hbm2ddl;

namespace FlexiCommerce.Installation
{
    public class InstallationManager
    {
        IConfigurationBuilder configurationBuilder;
        IPersister persister;
        ISessionProvider sessionProvider;

        public InstallationManager(IPersister persister, ISessionProvider sessionProvider, IConfigurationBuilder configurationBuilder)
        {
            this.persister = persister;
            this.sessionProvider = sessionProvider;
            this.configurationBuilder = configurationBuilder;
        }
        NHibernate.Cfg.Configuration cfg;
        protected NHibernate.Cfg.Configuration Cfg
        {
            get { return cfg ?? (cfg = configurationBuilder.BuildConfiguration()); }
        }

        public static string GetResourceString(string resourceKey)
        {
            Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceKey);
            StreamReader sr = new StreamReader(s);
            return sr.ReadToEnd();
        }

        const bool ConsoleOutput = false;
        const bool DatabaseExport = true;

        /// <summary>Executes sql create database scripts.</summary>
        public void Install()
        {
            SchemaExport exporter = new SchemaExport(Cfg);
            exporter.Create(ConsoleOutput, DatabaseExport);
        }

        public void ExportSchema(TextWriter output)
        {
            SchemaExport exporter = new SchemaExport(Cfg);
            exporter.Execute(ConsoleOutput, DatabaseExport, false, true, null, output);
        }

        public void DropDatabaseTables()
        {
            SchemaExport exporter = new SchemaExport(Cfg);
            exporter.Drop(ConsoleOutput, DatabaseExport);
        }

        public DatabaseStatus GetStatus()
        {
            DatabaseStatus status = new DatabaseStatus();

            UpdateConnection(status);
            UpdateSchema(status);
            UpdateItems(status);

            return status;
        }

        private void UpdateItems(DatabaseStatus status)
        {
            try
            {
                //status.StartPageID = host.DefaultSite.StartPageID;
                //status.RootItemID = host.DefaultSite.RootItemID;
                //status.StartPage = persister.Get(status.StartPageID);
                //status.RootItem = persister.Get(status.RootItemID);
                //status.IsInstalled = status.RootItem != null && status.StartPage != null;
                //TODO: Fix this status in installation manager.
                status.IsInstalled = true;
            }
            catch (Exception ex)
            {
                status.IsInstalled = false;
                status.ItemsError = ex.Message;
            }
        }

        private void UpdateSchema(DatabaseStatus status)
        {
            try
            {
                using (sessionProvider)
                {
                    ISession session = sessionProvider.OpenSession.Session;
                    status.Items = session.CreateCriteria(typeof(GenericPersistenceItem)).List().Count;
                    status.Details = session.CreateCriteria(typeof(ContentDetail)).List().Count;
                    status.DetailCollections = session.CreateCriteria(typeof(DetailCollection)).List().Count;
                }
                status.HasSchema = true;
            }
            catch (Exception ex)
            {
                status.HasSchema = false;
                status.SchemaError = ex.Message;
                status.SchemaException = ex;
            }
        }

        private void UpdateConnection(DatabaseStatus status)
        {
            try
            {
                using (IDbConnection conn = GetConnection())
                {
                    conn.Open();
                    conn.Close();
                    status.ConnectionType = conn.GetType().Name;
                }
                status.IsConnected = true;
                status.ConnectionError = null;
            }
            catch (Exception ex)
            {
                status.IsConnected = false;
                status.ConnectionError = ex.Message;
                status.ConnectionException = ex;
            }
        }

        /// <summary>Method that will checks the database. If something goes wrong an exception is thrown.</summary>
        /// <returns>A string with diagnostic information about the database.</returns>
        public string CheckDatabase()
        {
            using (sessionProvider)
            {
                ISession session = sessionProvider.OpenSession.Session;
                int itemCount = session.CreateCriteria(typeof(GenericPersistenceItem)).List().Count;
                int detailCount = session.CreateCriteria(typeof(ContentDetail)).List().Count;
                int detailCollectionCount = session.CreateCriteria(typeof(DetailCollection)).List().Count;
                return string.Format("Database OK, items: {0}, details: {1}, detail collections: {2}",
                                     itemCount, detailCount, detailCollectionCount);
            }
        }

        #region Helper Methods

        public IDbConnection GetConnection()
        {
            IDriver driver = GetDriver();

            IDbConnection conn = driver.CreateConnection();
            if (Cfg.Properties.ContainsKey(NHibernate.Cfg.Environment.ConnectionString))
                conn.ConnectionString = (string)Cfg.Properties[NHibernate.Cfg.Environment.ConnectionString];
            else if (Cfg.Properties.ContainsKey(NHibernate.Cfg.Environment.ConnectionStringName))
                conn.ConnectionString = ConfigurationManager.ConnectionStrings[(string)Cfg.Properties[NHibernate.Cfg.Environment.ConnectionStringName]].ConnectionString;
            else
                throw new Exception("Didn't find a confgiured connection string or connection string name in the nhibernate configuration.");
            return conn;
        }

        public IDbCommand GenerateCommand(CommandType type, string sqlString)
        {
            IDriver driver = GetDriver();
            return driver.GenerateCommand(type, new NHibernate.SqlCommand.SqlString(sqlString), new SqlType[0]);
        }

        private IDriver GetDriver()
        {
            string driverName = (string)Cfg.Properties[NHibernate.Cfg.Environment.ConnectionDriver];
            Type driverType = NHibernate.Util.ReflectHelper.ClassForName(driverName);
            return (IDriver)Activator.CreateInstance(driverType);
        }

        #endregion

    }
}
